const ProgressBar = require('./progress-bar');

/**
 * 最大异步执行数限制
 */
const ASYNC_MAX_LIMIT = 100;

/**
 * 队列最大长度临界值
 */
const QUEUE_MAX_LIMIT = 1e6;

/**
 * @type {Options}
 */
const defaultOptions = {
  asyncMax: 3,
  log: true,
  onComplete: null,
  onStop: null,
  onError: null,
};

/**
 * 有执行进度的任务队列
 * @param {Options} opts
 */
function TaskQueue(opts) {
  const progressBar = ProgressBar();
  /**
   * @type {Options}
   */
  const _options = Object.assign({}, defaultOptions);
  /**
   * 当前异步任务数
   */
  let _asyncCount = 0;
  /**
   * 等待队列
   * @type {Task[]}
   */
  let _pending = [];
  /**
   * @type {Task[]}
   */
  const _success = [];
  /**
   * @type {Task[]}
   */
  const _fail = [];
  let _isStop = true;
  let _isComplete;
  let _isWillComplete = false;

  /**
   * 开始回调
   */
  let _startResolve;
  /**
   * 停止回调
   */
  let _stopResolve;

  let timer;

  function startTimer() {
    if (!timer) {
      timer = setInterval(() => {
        progressBar.render();
      }, 500);
    }
  }

  function removeTimer() {
    if (timer) {
      clearInterval(timer);
      timer = null;
    }
  }

  const that = {
    /**
     * 初始化
     * @param {Options} opts 配置
     */
    config(opts = {}) {
      if (opts.asyncMax > ASYNC_MAX_LIMIT) {
        _log(`最大异步执行数不能超过：${ASYNC_MAX_LIMIT}`);
        opts.asyncMax = ASYNC_MAX_LIMIT;
      }
      for (const k in opts) {
        if (opts[k] !== undefined) {
          _options[k] = opts[k];
        }
      }
    },
    /**
     * 清空任务队列
     */
    clear() {
      return that.stop().then(() => {
        _pending = [];
      });
    },
    /**
     * 添加任务
     * @param {(data, index:Number) => Promise} promise 执行的任务
     * @param {String} describe 执行时显示的描述
     * @param {String} title 执行时显示的标题
     * @param {*} data 数据
     */
    add(promise, describe, title, data) {
      if (_pending.length >= QUEUE_MAX_LIMIT) {
        _log('队列最大长度过长会导致内存溢出');
      }
      progressBar.total(1, true);
      _pending.push({
        promise,
        describe,
        title,
        data,
      });
      if (_isComplete && !_isStop) {
        startTimer();
        _isComplete = false;
        _loop();
      }
    },
    /**
     * 开始
     */
    start() {
      startTimer();
      return new Promise((resolve, reject) => {
        if (_isStop) {
          _isStop = false;
          _startResolve = resolve;
          _log('任务开始', `任务并行数：${_options.asyncMax}`);
          _loop();
        } else if (_startResolve) {
          _startResolve = _concatFn(_startResolve, resolve);
          _log('任务已在进行中');
        } else {
          reject(new Error('任务队列已完成，请重置后再开始'));
        }
      });
    },
    /**
     * 暂停
     */
    stop() {
      return new Promise((resolve, reject) => {
        if (_isComplete) {
          reject(new Error('队列已完成，无法暂停'));
        } else if (_isStop) {
          if (_stopResolve) {
            _stopResolve = _concatFn(_stopResolve, resolve);
          } else { // 已是暂停状态
            _log('队列已是暂停状态');
            resolve();
          }
        } else {
          _isStop = true;
          _stopResolve = resolve;
        }
      });
    },
    /**
     * 任务队列的长度
     */
    length() {
      return _pending.length + _success.length + _fail.length;
    },
    /**
     * 是否未开始
     */
    isStop() {
      return _isStop;
    },
    /**
     * 是否即将完成
     */
    isWillComplete() {
      return _isWillComplete;
    },
    /**
     * 是否已经完成
     */
    isComplete() {
      return _isComplete;
    },
    /**
     * 进度条实例
     */
    progressBar,
  };

  that.config(opts);


  /**
   * 合并function
   * @param {Function} mainFn
   * @param {Function} addFn
   */
  function _concatFn(mainFn, addFn) {
    return (...args) => {
      mainFn(...args);
      addFn(...args);
    };
  }

  /**
   * 循环
   */
  async function _loop() {
    const task = _pending.shift();
    if (task) {
      progressBar.title(task.title)
        .describe(task.describe).render();
      _asyncCount += 1;
      if (_asyncCount >= _options.asyncMax) {
        await _run();
      } else {
        _run();
        _next();
      }
    } else if (_asyncCount === 0) {
      _onComplete();
    } else {
      _isWillComplete = true;
    }

    async function _run() {
      const now = new Date();
      try {
        await task.promise(task.data);
        _success.push(task);
      } catch (err) {
        _fail.push(task);
        if (_options.onError) {
          _options.onError(err, task);
        }
      }
      if (new Date() - now < 30) {
        // 视为已经做过的任务
        progressBar.invalid();
      }
      progressBar.add();
      await _next(task);
    }
  }

  /**
   * 执行下一个任务
   * @param {Task} prev 上一个任务
   */
  async function _next(prev) {
    if (prev) {
      _asyncCount -= 1;
      if (_isWillComplete && _asyncCount === 0) {
        _onComplete();
      }
    }
    if (_isStop) {
      if (_asyncCount === 0) {
        _onStop();
      }
    } else {
      await _loop();
    }
  }

  /**
   * 任务队列停止执行
   */
  function _onStop() {
    removeTimer();
    progressBar.describe('已暂停').render(true);
    progressBar.quit();
    _isStop = true;
    if (_options.onStop) _options.onStop();
    if (_stopResolve) {
      _stopResolve();
      _stopResolve = null;
    }
    if (_startResolve) {
      _startResolve();
      _startResolve = null;
    }
  }

  /**
   * 任务队列完成执行
   */
  function _onComplete() {
    removeTimer();
    // 完全的完成了
    const isComplete = _pending.length === 0;
    if (isComplete) progressBar.quit();
    _isComplete = true;
    _isWillComplete = false;
    if (_options.onComplete) _options.onComplete(isComplete);
    if (_startResolve) {
      _startResolve(isComplete);
      _startResolve = null;
    }
  }

  function _log(...args) {
    // eslint-disable-next-line no-console
    console.log('[queue]', ...args);
  }

  return that;
}


module.exports = TaskQueue;

/**
 * 任务类
 * @typedef Task
 * @property {(data,index:Number) => Promise} promise 任务
 * @property {String} describe 描述
 * @property {String} title 标题
 * @property {String} data 携带的数据
 */
// Task


/**
 * 任务队列配置
 * @typedef Options
 * @property {Number} asyncMax 任务最大异步执行数
 * @property {Boolean} log 是否显示日志
 * @property {(isComplete:Boolean) => void} onComplete 完成回调
 * @property {(index:Number) => void} onStop 暂停回调
 * @property {(error:Error,task:Task) => void} onError 报错回调
 */
// Options
